package com.uduemc.biso.node.web.controller;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import com.uduemc.biso.core.extities.center.Host;
import com.uduemc.biso.core.utils.CryptoJava;
import com.uduemc.biso.core.utils.RedisUtil;
import com.uduemc.biso.node.core.entities.HRepertory;
import com.uduemc.biso.node.core.property.GlobalProperties;
import com.uduemc.biso.node.core.utils.SitePathUtil;
import com.uduemc.biso.node.web.component.AssetsResponseComponent;
import com.uduemc.biso.node.web.component.RequestHolder;
import com.uduemc.biso.node.web.config.SpringContextUtil;
import com.uduemc.biso.node.web.exception.NotFound404Exception;
import com.uduemc.biso.node.web.service.byfeign.RepertoryService;
import com.uduemc.biso.node.web.utils.VideoUtil;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

@Controller
public class AssetsResourceController {

    @Resource
    private AssetsResponseComponent assetsResponseComponent;

    @Resource
    private GlobalProperties globalProperties;

    @Resource
    private RequestHolder requestHolder;

    @Resource
    private RepertoryService repertoryServiceImpl;

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private SpringContextUtil springContextUtil;

    /**
     * 资源库图片的展示
     *
     * @param year
     * @param mouth
     * @param day
     * @param request
     * @param response
     * @throws Exception
     */
    @GetMapping({"/assets/resource/images/repertory/{year:\\d+}/{mouth:\\d+}/{day:\\d+}/{filename}",
            "/site/*/assets/resource/images/repertory/{year:\\d+}/{mouth:\\d+}/{day:\\d+}/{filename}"})
    @ResponseBody
    public void resourceImage(@PathVariable("year") int year, @PathVariable("mouth") int mouth, @PathVariable("day") int day,
                              @PathVariable("filename") String filename, HttpServletRequest request, HttpServletResponse response) throws Exception {
        filename = filename.substring(0, filename.lastIndexOf("."));
        String suffix = request.getRequestURI().substring(request.getRequestURI().lastIndexOf("."), request.getRequestURI().length());
        filename = filename + suffix;
        // 资源图片相对路径
        String filePath = "repertory/" + year + "/" + mouth + "/" + day + "/" + filename;
        String basePath = globalProperties.getSite().getBasePath();
        Host host = requestHolder.getHost();
        // 通过 basePath 以及 code 获取 用户的目录
        String userPathByCode = SitePathUtil.getUserPathByCode(basePath, host.getRandomCode());

        // 资源图片完整路径
        String filefullpath = userPathByCode + "/" + filePath;
        File file = new File(filefullpath);
        if (!file.isFile()) {
            throw new NotFound404Exception("未能找到资源仓库中的文件！ filefullpath: " + filefullpath);
        }

        // 允许放行的
        String key = globalProperties.getSiteRedisKey().getAllowRepertoryByFilePath(host.getId(), filePath);
        HRepertory hRepertory = (HRepertory) redisUtil.get(key);
        if (hRepertory != null && hRepertory.getType().shortValue() == (short) 0) {
            assetsResponseComponent.imageResponse(filefullpath, response);
            return;
        }

        // 不允许放行的，放置渗透
        String noAllowKey = globalProperties.getSiteRedisKey().getUnallowRepertoryByFilePath(host.getId());
        boolean sHasKey = redisUtil.sHasKey(noAllowKey, filePath);
        if (sHasKey) {
            if (springContextUtil.prod()) {
                throw new NotFound404Exception("防止渗透，缓存中取出不存在的资源路径！");
            }
        }

        // 通过 hostId 以及 日期、filename 获取 HRepertory 数据
        SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date date;
        try {
            date = sDateFormat.parse(year + "-" + mouth + "-" + day);
        } catch (ParseException e) {
            e.printStackTrace();
            throw new NotFound404Exception("格式化日期时间出现异常！");
        }

        HRepertory findOne = repertoryServiceImpl.findByOriginalFilenameAndToDay(host.getId(), date.getTime(), filename);
        if (findOne == null || findOne.getType().shortValue() != (short) 0) {
            redisUtil.sSetAndTime(noAllowKey, 60 * 30L, filePath);
            throw new NotFound404Exception("通过 hostId、日期、filename 从数据库中无法取出资源数据！或者获取的数据不为图片");
        } else {
            redisUtil.set(key, findOne, 60 * 60L);
            assetsResponseComponent.imageResponse(filefullpath, response);
        }

    }

    /**
     * 资源库视频播放
     *
     * @param year
     * @param mouth
     * @param day
     * @param request
     * @param response
     * @throws Exception
     */
    @GetMapping({"/assets/resource/video/repertory/{year:\\d+}/{mouth:\\d+}/{day:\\d+}/{filename}",
            "/site/*/assets/resource/video/repertory/{year:\\d+}/{mouth:\\d+}/{day:\\d+}/{filename}"})
    @ResponseBody
    public void resourceVideo(@PathVariable("year") int year,
                              @PathVariable("mouth") int mouth,
                              @PathVariable("day") int day,
                              @PathVariable("filename") String filename,
                              @RequestParam(value = "tk", required = false, defaultValue = "") String token,
                              @RequestParam(value = "rd", required = false, defaultValue = "") String rd,
                              HttpServletRequest request,
                              HttpServletResponse response) throws Exception {
        Long repertoryId = -1L;
        if (StrUtil.isNotBlank(token)) {
            if (NumberUtil.isNumber(token)) {
                repertoryId = Long.valueOf(token);
            } else {
                token = CryptoJava.de(token);
                if (NumberUtil.isNumber(token)) {
                    repertoryId = Long.valueOf(token);
                }
            }
        }
        if (repertoryId == null || repertoryId.longValue() < 1) {
            throw new NotFound404Exception("未能找到资源仓库中的视频文件！ token: " + token + ", repertoryId: " + repertoryId);
        }

        // 查看 session 中的 rd 是否一致
        HttpSession session = request.getSession();
        String attribute = (String) session.getAttribute("VideoSrc.h_repertory.id." + repertoryId);
        if (StrUtil.isBlank(attribute) || !attribute.equals(rd)) {
//			throw new NotFound404Exception("请求地址的 rd 与 session 中的 VideoSrc.h_repertory.id." + repertoryId + " 不匹配, session.rd: " + attribute + ", rd: " + rd);
        }

        filename = filename.substring(0, filename.lastIndexOf("."));
        String suffix = request.getRequestURI().substring(request.getRequestURI().lastIndexOf("."), request.getRequestURI().length());
        filename = filename + suffix;
        // 资源图片相对路径
        String filePath = "repertory/" + year + "/" + mouth + "/" + day + "/" + filename;
        String basePath = globalProperties.getSite().getBasePath();
        Host host = requestHolder.getHost();
        // 通过 basePath 以及 code 获取 用户的目录
        String userPathByCode = SitePathUtil.getUserPathByCode(basePath, host.getRandomCode());

        // 资源图片完整路径
        String filefullpath = userPathByCode + "/" + filePath;
        File file = new File(filefullpath);
        if (!file.isFile()) {
            throw new NotFound404Exception("未能找到资源仓库中的文件！ filefullpath: " + filefullpath);
        }

        // 允许放行的
        String key = globalProperties.getSiteRedisKey().getAllowRepertoryByFilePath(host.getId(), filePath);
        HRepertory hRepertory = (HRepertory) redisUtil.get(key);
        if (hRepertory != null && hRepertory.getType().shortValue() == (short) 0) {
            VideoUtil.play(filefullpath, request, response);
            return;
        }

        // 不允许放行的，放置渗透
        String noAllowKey = globalProperties.getSiteRedisKey().getUnallowRepertoryByFilePath(host.getId());
        boolean sHasKey = redisUtil.sHasKey(noAllowKey, filePath);
        if (sHasKey) {
            if (springContextUtil.prod()) {
                throw new NotFound404Exception("防止渗透，缓存中取出不存在的资源路径！");
            }
        }

        // 通过 hostId 以及 日期、filename 获取 HRepertory 数据
        SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date date;
        try {
            date = sDateFormat.parse(year + "-" + mouth + "-" + day);
        } catch (ParseException e) {
            e.printStackTrace();
            throw new NotFound404Exception("格式化日期时间出现异常！");
        }

        HRepertory findOne = repertoryServiceImpl.findByOriginalFilenameAndToDay(host.getId(), date.getTime(), filename);
        if (findOne == null || repertoryId.longValue() != findOne.getId().longValue() || findOne.getType().shortValue() != (short) 5) {
            redisUtil.sSetAndTime(noAllowKey, 60 * 30L, filePath);
            throw new NotFound404Exception("通过 hostId、日期、filename 从数据库中无法取出资源数据！或者获取的数据不为图片");
        } else {
            redisUtil.set(key, findOne, 15 * 60L);
            VideoUtil.play(filefullpath, request, response);
        }

    }

    /**
     * 资源库文件的下载
     *
     * @param year
     * @param month
     * @param fileName
     * @param request
     * @param response
     * @throws Exception
     */
    @GetMapping({"/assets/resource/download/repertory/{year:\\d+}/{mouth:\\d+}/{day:\\d+}/{filename}",
            "/site/*/assets/resource/download/repertory/{year:\\d+}/{mouth:\\d+}/{day:\\d+}/{filename}"})
    @ResponseBody
    public void resourceDownload(@PathVariable("year") int year, @PathVariable("mouth") int mouth, @PathVariable("day") int day,
                                 @PathVariable("filename") String filename, HttpServletRequest request, HttpServletResponse response) throws Exception {
        filename = filename.substring(0, filename.lastIndexOf("."));
        String suffix = request.getRequestURI().substring(request.getRequestURI().lastIndexOf("."), request.getRequestURI().length());

        filename = filename + suffix;

        // 资源图片相对路径
        String filePath = "repertory/" + year + "/" + mouth + "/" + day + "/" + filename;
        String basePath = globalProperties.getSite().getBasePath();
        Host host = requestHolder.getHost();
        // 通过 basePath 以及 code 获取 用户的目录
        String userPathByCode = SitePathUtil.getUserPathByCode(basePath, host.getRandomCode());

        // 资源图片完整路径
        String filefullpath = userPathByCode + "/" + filePath;
        File file = new File(filefullpath);
        if (!file.isFile()) {
            throw new NotFound404Exception("未能找到资源仓库中的文件！ filefullpath: " + filefullpath);
        }

        // 允许放行的
        String key = globalProperties.getSiteRedisKey().getAllowRepertoryByFilePath(host.getId(), filePath);
        HRepertory hRepertory = (HRepertory) redisUtil.get(key);
        if (hRepertory != null && hRepertory.getType().shortValue() == (short) 1) {
            // 下载
            try (InputStream inputStream = new FileInputStream(filefullpath); OutputStream outputStream = response.getOutputStream();) {
                String outFilename = hRepertory.getOriginalFilename();
                // response header
                response.setHeader("Accept-Ranges", "bytes");
                response.setHeader("Content-Length", String.valueOf(hRepertory.getSize()));
                response.setHeader("Content-Transfer-Encoding", "binary");
                // 解决java web中safari浏览器下载后文件中文乱码问题
                response.setContentType("application/octet-stream");
                String userAgent = request.getHeader("User-Agent").toLowerCase();
                if (userAgent.contains("msie") || userAgent.contains("trident")) {
                    outFilename = URLEncoder.encode(outFilename, "UTF-8");
                } else {
                    outFilename = new String(outFilename.getBytes("UTF-8"), "ISO-8859-1");
                }
                response.addHeader("Content-Disposition", "attachment;filename=" + outFilename);
                IOUtils.copy(inputStream, outputStream);
                outputStream.flush();
            }
            return;
        }

        // 不允许放行的，放置渗透
        String noAllowKey = globalProperties.getSiteRedisKey().getUnallowRepertoryByFilePath(host.getId());
        boolean sHasKey = redisUtil.sHasKey(noAllowKey, filePath);
        if (sHasKey) {
            throw new NotFound404Exception("防止渗透，缓存中取出不存在的资源路径！");
        }

        // 通过 hostId 以及 日期、filename 获取 HRepertory 数据
        SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date date;
        try {
            date = sDateFormat.parse(year + "-" + mouth + "-" + day);
        } catch (ParseException e) {
            e.printStackTrace();
            throw new NotFound404Exception("格式化日期时间出现异常！");
        }

        HRepertory findOne = repertoryServiceImpl.findByOriginalFilenameAndToDay(host.getId(), date.getTime(), filename);
        if (findOne == null || findOne.getType().shortValue() != (short) 1) {
            redisUtil.sSetAndTime(noAllowKey, 60 * 30L, filePath);
            throw new NotFound404Exception("通过 hostId、日期、filename 从数据库中无法取出资源数据！或者获取的数据不为文件");
        } else {
            redisUtil.set(key, findOne, 60 * 60L);
            // 下载
            try (InputStream inputStream = new FileInputStream(filefullpath); OutputStream outputStream = response.getOutputStream();) {
                String outFilename = hRepertory.getOriginalFilename();
                // response header
                response.setHeader("Accept-Ranges", "bytes");
                response.setHeader("Content-Length", String.valueOf(hRepertory.getSize()));
                response.setHeader("Content-type", "text/html;charset=gbk");
                response.setHeader("Content-Transfer-Encoding", "binary");
                response.setContentType("application/x-download");
                response.addHeader("Content-Disposition", "attachment;filename=" + URLUtil.encode(outFilename));
                IOUtils.copy(inputStream, outputStream);
                outputStream.flush();
            }
        }
    }

    /**
     * 安全资源库文件的下载
     *
     * @param year
     * @param month
     * @param fileName
     * @param request
     * @param response
     * @throws Exception
     */
    @GetMapping({"/assets/resource/download/secure/{token}", "/site/*/assets/resource/download/secure/{token}"})
    @ResponseBody
    public void resourceDownloadSecure(@PathVariable("token") String token, @RequestParam("rd") String rd, HttpServletRequest request,
                                       HttpServletResponse response) throws Exception {
        Long repertoryId = -1L;
        if (NumberUtil.isNumber(token)) {
            repertoryId = Long.valueOf(token);
        } else {
            token = CryptoJava.de(token);
            if (NumberUtil.isNumber(token)) {
                repertoryId = Long.valueOf(token);
            }
        }
        if (repertoryId == null || repertoryId.longValue() < 1) {
            throw new NotFound404Exception("未能找到资源仓库中的文件！ token: " + token + ", repertoryId: " + repertoryId);
        }

        // 查看 session 中的 rd 是否一致
        HttpSession session = request.getSession();
        String attribute = (String) session.getAttribute("DownloadHref.h_repertory.id." + repertoryId);
        if (StrUtil.isBlank(attribute) || !attribute.equals(rd)) {
//			throw new NotFound404Exception("请求地址的 rd 与 session 中的 DownloadHref.h_repertory.id." + repertoryId + " 不匹配, session.rd: " + attribute + ", rd: " + rd);
        }

        Host host = requestHolder.getHost();
        HRepertory hRepertory = repertoryServiceImpl.getRepertoryByHostIdAndId(host.getId(), repertoryId);
        if (hRepertory == null) {
            throw new NotFound404Exception("通过 hostId、repertoryId 从数据库中无法取出资源数据！hostId、repertoryId: " + host.getId() + "、" + repertoryId);
        }

        // 通过 basePath 以及 code 获取 用户的目录
        String basePath = globalProperties.getSite().getBasePath();
        String userPathByCode = SitePathUtil.getUserPathByCode(basePath, host.getRandomCode());

        // 资源图片完整路径
        String filefullpath = userPathByCode + "/" + hRepertory.getFilepath();
        File file = new File(filefullpath);
        if (!file.isFile()) {
            throw new NotFound404Exception("未能找到资源仓库中的文件！ filefullpath: " + filefullpath);
        }

        // 下载
        try (InputStream inputStream = new FileInputStream(filefullpath); OutputStream outputStream = response.getOutputStream();) {
            String outFilename = hRepertory.getOriginalFilename();
            // response header
            response.setHeader("Accept-Ranges", "bytes");
            response.setHeader("Content-Length", String.valueOf(hRepertory.getSize()));
            response.setHeader("Content-type", "text/html;charset=gbk");
            response.setHeader("Content-Transfer-Encoding", "binary");
            response.setContentType("application/x-download");
            response.addHeader("Content-Disposition", "attachment;filename=" + URLUtil.encode(outFilename));
            IOUtils.copy(inputStream, outputStream);
            outputStream.flush();
        }
        return;
    }

    @GetMapping({"/assets/resource/preview/pdf/{tk}/{name}", "/site/*/assets/resource/preview/pdf/{tk}/{name}"})
    @ResponseBody
    public void resourcepReviewPdf(@PathVariable("tk") String tk, @RequestParam("rd") String rd, HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        Long repertoryId = -1L;
        if (NumberUtil.isNumber(tk)) {
            repertoryId = Long.valueOf(tk);
        } else {
            tk = CryptoJava.de(tk);
            if (NumberUtil.isNumber(tk)) {
                repertoryId = Long.valueOf(tk);
            }
        }
        if (repertoryId == null || repertoryId.longValue() < 1) {
            throw new NotFound404Exception("未能找到资源仓库中的文件！ tk: " + tk + ", repertoryId: " + repertoryId);
        }

        // 查看 session 中的 rd 是否一致
        HttpSession session = request.getSession();
        String attribute = (String) session.getAttribute("PreviewPdfHref.h_repertory.id." + repertoryId);
        if (StrUtil.isBlank(attribute) || !attribute.equals(rd)) {
//			throw new NotFound404Exception("请求地址的 rd 与 session 中的 DownloadHref.h_repertory.id." + repertoryId + " 不匹配, session.rd: " + attribute + ", rd: " + rd);
        }

        Host host = requestHolder.getHost();
        HRepertory hRepertory = repertoryServiceImpl.getRepertoryByHostIdAndId(host.getId(), repertoryId);
        if (hRepertory == null) {
            throw new NotFound404Exception("通过 hostId、repertoryId 从数据库中无法取出资源数据！hostId、repertoryId: " + host.getId() + "、" + repertoryId);
        }

        // 通过 basePath 以及 code 获取 用户的目录
        String basePath = globalProperties.getSite().getBasePath();
        String userPathByCode = SitePathUtil.getUserPathByCode(basePath, host.getRandomCode());

        // 资源图片完整路径
        String filefullpath = userPathByCode + "/" + hRepertory.getFilepath();
        File file = new File(filefullpath);
        if (!file.isFile()) {
            throw new NotFound404Exception("未能找到资源仓库中的文件！ filefullpath: " + filefullpath);
        }

        // 预览
        try (InputStream inputStream = new FileInputStream(filefullpath); OutputStream outputStream = response.getOutputStream();) {
            // response header
            response.setHeader("Accept-Ranges", "bytes");
            response.setHeader("Content-Length", String.valueOf(hRepertory.getSize()));
            response.setHeader("Content-Transfer-Encoding", "binary");
            response.setContentType("application/pdf");

            byte[] b = new byte[1024];
            while ((inputStream.read(b)) != -1) {
                outputStream.write(b);
            }
            outputStream.flush();
        }
        return;
    }
}
